package com.zhiche.lisa.core.utils.geo.BaiDuMap.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zhiche.lisa.core.supports.BaseException;
import com.zhiche.lisa.core.utils.HttpClientUtil;
import com.zhiche.lisa.core.utils.geo.BaiDuMap.config.BaiDuMapCoreConfig;
import com.zhiche.lisa.core.utils.geo.BaiDuMap.config.BaiDuMapGeoConfig;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Component
public class BaiDuMapGeocodeUtil {

    private static final Logger logger = LoggerFactory.getLogger(BaiDuMapGeocodeUtil.class);


    //编码格式。发送编码格式统一用UTF-8
    private static String ENCODING = "UTF-8";

    //连接超时时间
    private static int socketTimeout = 10000;

    //静态注入的方式
    private static BaiDuMapCoreConfig baiDuMapCoreConfig;

    private static BaiDuMapGeoConfig baiDuMapGeoConfig;

    @Autowired
    public void setBaiDuMapCoreConfig(BaiDuMapCoreConfig baiDuMapCoreConfig) {
        BaiDuMapGeocodeUtil.baiDuMapCoreConfig = baiDuMapCoreConfig;
    }

    @Autowired
    public void setBaiDuMapGeoConfig(BaiDuMapGeoConfig baiDuMapGeoConfig) {
        BaiDuMapGeocodeUtil.baiDuMapGeoConfig = baiDuMapGeoConfig;
    }

    /**
     * 获取地理编码--经纬度
     * @param address 待解析的地址。最多支持84个字节。可以输入三种样式的值，分别是：1、标准的结构化地址信息，如北京市海淀区上地十街十号 【推荐，地址结构越完整，解析精度越高】
      2、支持“*路与*路交叉口”描述方式，如北一环路和阜阳路的交叉路口 第二种方式并不总是有返回结果，只有当地址库中存在该地址描述时才有返回。
     * lat	纬度值  lng 	经度值
     * @return
     * @throws BaseException
     */
    public static Map<String,Double> addressToGPS(String address) throws BaseException {
        String addressEncode = address;
        Map<String,Double> map = Maps.newHashMap();

        try {
            addressEncode = URLEncoder.encode(address,ENCODING);
        } catch (UnsupportedEncodingException e) {
            logger.error("addressToGPS encode error: {}", e);
            throw new BaseException("地址编码错误");
        }
        logger.info("addressToGPS addressStr:{} encoded: {}", address, addressEncode);
        logger.info("address to gps url :",baiDuMapGeoConfig.getAddressToGPSUrl());
        String url = String.format(baiDuMapGeoConfig.getAddressToGPSUrl(), addressEncode,baiDuMapCoreConfig.getApiKey());
        try {
            String strReturn = HttpClientUtil.get(url,null,socketTimeout);
            JSONObject jsonObject = JSON.parseObject(strReturn);
            int status = jsonObject.getInteger("status");
            String msg = BaiDuMapStatusEnum.getTest(status);
            if(status == 0){
                JSONObject location = jsonObject.getJSONObject("result").getJSONObject("location");
                if(!Objects.isNull(location)){
                    map.put("lng",location.getDouble("lng"));
                    map.put("lat",location.getDouble("lat"));
                }
            }
            else{
                throw new BaseException(msg);
            }
        } catch (Exception e) {
            logger.error("addressToGPS URL error: {}", e);
            throw new BaseException("地址转GPS错误");
        }

       return map;
    }


    /**
     * 逆地理编码
     * @param location 根据经纬度坐标获取地址。支持批量，多组坐标间用|分隔，单次请求最多解析20组坐标。超过20组取前20组解析。批量解析需使用batch参数。批量解析仅召回行政区划数据。
     * 表达式：lat<纬度>,lng<经度>
     * @param pois 是否召回传入坐标周边的poi，0为不召回，1为召回。当值为1时，默认显示周边1000米内的poi。
     * @return
     * @throws BaseException
     */
    public static String GPSToAddress(String location,int pois) throws BaseException{
        String strAddress = GPSToAddressBatch(location,false,pois);
        return  strAddress;
    }

    /**
     * 逆地理编码
     * @param location location 根据经纬度坐标获取地址。支持批量，多组坐标间用|分隔，单次请求最多解析20组坐标。超过20组取前20组解析。批量解析需使用batch参数。批量解析仅召回行政区划数据。
     * 表达式：lat<纬度>,lng<经度>
     * @param bathch 批量查询
     * @param pois 是否召回传入坐标周边的poi，0为不召回，1为召回。当值为1时，默认显示周边1000米内的poi。
     * @return
     * @throws BaseException
     */
    public static String GPSToAddressBatch(String location,Boolean bathch, int pois) throws BaseException{
        String strAddress = null;
        String url = String.format(baiDuMapGeoConfig.getGpsToAddressUrl(),location,bathch,pois,baiDuMapCoreConfig.getApiKey());
        try {
            String strReturn = HttpClientUtil.get(url,null,socketTimeout);
            JSONObject jsonObject = JSON.parseObject(strReturn);
            int status = jsonObject.getInteger("status");
            String msg = BaiDuMapStatusEnum.getTest(status);
            if(status == 0){
                strAddress = jsonObject.getJSONObject("result").getString("formatted_address");
            }
            else{
                throw new BaseException(msg);
            }
        } catch (Exception e) {
            logger.error("addressToGPS URL error: {}", e);
            throw new BaseException("地址转GPS错误");
        }

        return strAddress;
    }


    /**
     *
     * @param coords 需转换的源坐标，多组坐标以“；”分隔（经度，纬度）
     * @param from 源坐标类型：
        1：GPS设备获取的角度坐标，wgs84坐标;
        2：GPS获取的米制坐标、sogou地图所用坐标;
        3：google地图、soso地图、aliyun地图、mapabc地图和amap地图所用坐标，国测局（gcj02）坐标;
        4：3中列表地图坐标对应的米制坐标;
        5：百度地图采用的经纬度坐标;
        6：百度地图采用的米制坐标;
        7：mapbar地图坐标;
        8：51地图坐标
     * @param to 目标坐标类型：
        3：国测局（gcj02）坐标;
        4：3中对应的米制坐标;
        5：bd09ll(百度经纬度坐标);
        6：bd09mc(百度米制经纬度坐标)
     * @return
     * @throws BaseException
     *
     */
    public static List<Map<String,Double>> geoConv(String coords, int from, int to) throws BaseException{
        List<Map<String,Double>> lists = Lists.newArrayList();
        String url = String.format(baiDuMapGeoConfig.getGeoconvUrl(),coords,from,to,baiDuMapCoreConfig.getApiKey());
        try {
            String strReturn = HttpClientUtil.get(url,null,socketTimeout);
            JSONObject jsonObject = JSON.parseObject(strReturn);
            int status = jsonObject.getInteger("status");
            String msg = BaiDuMapStatusEnum.getTest(status);
            if(status == 0){
                JSONArray jsonArray = jsonObject.getJSONArray("result");
                if(!CollectionUtils.isEmpty(jsonArray)){
                    jsonArray.forEach(object1 -> {
                        JSONObject jsonObject1 = (JSONObject) object1;
                        Map<String,Double> map = Maps.newHashMap();
                        map.put("lng",jsonObject1.getDouble("x"));
                        map.put("lat",jsonObject1.getDouble("y"));
                        lists.add(map);
                    });
                }
            }
            else{
                throw new BaseException(msg);
            }
        } catch (Exception e) {
            logger.error("geoConv error: {}", e);
            throw new BaseException("地图坐标转换错误");
        }

        return lists;
    }


}
